#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <errno.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <openssl/pem.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <fcntl.h>
#include <sys/stat.h>

#include "utils.h"
#include "debug.h"

static void thread_signal_handler(int sig)
{
}

static void *pvThreadFunction(void *psThreadInfoVoid)
{
    utils_thread_ts *psThreadInfo;

    if (!psThreadInfoVoid)
    {
        LOGE("invalid parameters");
        return NULL;
    }

    psThreadInfo = (utils_thread_ts *) psThreadInfoVoid;

    pthread_mutex_lock(&psThreadInfo->mtx);
    if (psThreadInfo->running_state == EN_THREAD_STOPPED)
    {
        psThreadInfo->running_state = EN_THREAD_RUNNING;
    }
    pthread_mutex_unlock(&psThreadInfo->mtx);

    if (psThreadInfo->tfunc)
    {
        psThreadInfo->tfunc(psThreadInfo);
    }

    pthread_exit(NULL);
}

/*****************************************************************************
 函 数 名  : utils_thread_start
 功能描述  : 创建一个线程，线程函数:prThreadFunction，线程参数:psThreadInfo
 输入参数  : threadfunc prThreadFunction
             utils_thread_ts *psThreadInfo
 输出参数  : 无
 返 回 值  :
 调用函数  :
 被调函数  :

 修改历史      :
  1.日    期   : 2020年5月20日
    作    者   : thomas
    修改内容   : 新生成函数

*****************************************************************************/
utils_status_te utils_thread_start(threadfunc prThreadFunction, utils_thread_ts *psThreadInfo)
{
    int ret;
    struct sigaction sa;
    pthread_attr_t attr;
    static int iFirstTime = 1;

    if (!prThreadFunction || !psThreadInfo)
    {
        LOGE("invalid parameters");
        return EN_UTILS_ERR_FAILED;
    }

    if (0 != pthread_mutex_init(&psThreadInfo->mtx, NULL))
    {
        LOGE("mutex init error");
        return EN_UTILS_ERR_FAILED;
    }

    pthread_mutex_lock(&psThreadInfo->mtx);
    psThreadInfo->running_state = EN_THREAD_STOPPED;
    psThreadInfo->tfunc = prThreadFunction;
    pthread_mutex_unlock(&psThreadInfo->mtx);

    if (iFirstTime)
    {
        /*设置信号处理函数，每个进程仅注册一次，开启其他线程时，继承此信号处理函数*/
        sa.sa_handler = thread_signal_handler;
        sa.sa_flags = 0;
        sigemptyset(&sa.sa_mask);

        if (sigaction(THREAD_SIGNAL, &sa, NULL) == -1)
        {
            LOGE("sigaction error:%s", strerror(errno));
        }
        else
        {
            iFirstTime = 0;
        }
    }

    pthread_attr_init(&attr);
    if (psThreadInfo->detach_state == EN_THREAD_DETACHED)
    {
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    }
    else
    {
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    }

    if (0 != (ret = pthread_create(&psThreadInfo->tid, &attr, pvThreadFunction, psThreadInfo)))
    {
        LOGE("pthread_create error:%s", strerror(ret));
        return EN_UTILS_ERR_FAILED;
    }

    pthread_attr_destroy(&attr);

    return EN_UTILS_OK;
}

/*****************************************************************************
 函 数 名  : utils_thread_stop
 功能描述  : 停止一个线程，并释放相关资源
 输入参数  : utils_thread_ts *psThreadInfo
 输出参数  : 无
 返 回 值  :
 调用函数  :
 被调函数  :

 修改历史      :
  1.日    期   : 2020年5月20日
    作    者   : thomas
    修改内容   : 新生成函数

*****************************************************************************/
utils_status_te utils_thread_stop(utils_thread_ts *psThreadInfo)
{
    int ret;

    if (!psThreadInfo)
    {
        LOGE("invalid parameters");
        return EN_UTILS_ERR_FAILED;
    }

    pthread_mutex_lock(&psThreadInfo->mtx);
    if (psThreadInfo->running_state == EN_THREAD_RUNNING || psThreadInfo->running_state == EN_THREAD_STOPPED)
    {
        psThreadInfo->running_state = EN_THREAD_STOPPING;
    }
    pthread_mutex_unlock(&psThreadInfo->mtx);

    /* 发送信号打断某些阻塞的系统调用，例如read, write等，以便线程退出循环*/
    //检查指定线程是否存在
    if (0 == pthread_kill(psThreadInfo->tid, 0))
    {
        if (0 != (ret = pthread_kill(psThreadInfo->tid, THREAD_SIGNAL)))
        {
            LOGE("pthread_kill error:%s", strerror(ret));
        }
    }
    else
    {
        LOGE("thread with tid[%ld] no exist", (unsigned long int) (psThreadInfo->tid));
        return EN_UTILS_ERR_FAILED;
    }

    if (psThreadInfo->detach_state == EN_THREAD_JOINABLE)
    {
        if (0 != (ret = pthread_join(psThreadInfo->tid, NULL)))
        {
            LOGE("pthread_join error:%s", strerror(ret));
            return EN_UTILS_ERR_FAILED;
        }
    }
    else if (psThreadInfo->detach_state == EN_THREAD_DETACHED)
    {
        return EN_UTILS_ERR_FAILED;
    }

    //释放资源
    if (psThreadInfo->detach_state == EN_THREAD_DETACHED)
    {
        pthread_mutex_destroy(&psThreadInfo->mtx);
    }
    else if (psThreadInfo->detach_state == EN_THREAD_JOINABLE)
    {
        pthread_mutex_lock(&psThreadInfo->mtx);
        psThreadInfo->running_state = EN_THREAD_STOPPED;
        pthread_mutex_unlock(&psThreadInfo->mtx);
        pthread_mutex_destroy(&psThreadInfo->mtx);
    }

    return EN_UTILS_OK;
}

int utils_check_running(utils_thread_ts *psThreadInfo)
{
    int ret;

    pthread_mutex_lock(&psThreadInfo->mtx);
    ret = (psThreadInfo->running_state == EN_THREAD_RUNNING);
    pthread_mutex_unlock(&psThreadInfo->mtx);

    return ret;
}

utils_status_te utils_cond_init(condition_val_ts *pcond)
{
    int ret;
    pthread_mutexattr_t attr;

    if (pcond == NULL)
    {
        LOGE("invalid parameters");
        return EN_UTILS_ERR_FAILED;
    }

    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
    ret = pthread_mutex_init(&pcond->mutex, &attr);
    if (ret != 0)
    {
        LOGE("mute init failed:%s", strerror(ret));
        return EN_UTILS_ERR_FAILED;
    }
    pthread_mutexattr_destroy(&attr);

    ret = pthread_cond_init(&pcond->cond_data_available, NULL);
    if (ret != 0)
    {
        LOGE("cond variable init failed:%s", strerror(ret));
        return EN_UTILS_ERR_FAILED;
    }

    ret = pthread_mutex_lock(&pcond->mutex);
    if (ret != 0)
    {
        LOGE("mutex lock error");
        return EN_UTILS_ERR_FAILED;
    }

    pcond->val = NULL;
    pcond->waittick = 0;
    pcond->released = 0;

    ret = pthread_mutex_unlock(&pcond->mutex);
    if (ret != 0)
    {
        LOGE("mutex unlock error");
        return EN_UTILS_ERR_FAILED;
    }

    return EN_UTILS_OK;
}

utils_status_te utils_cond_destroy(condition_val_ts *pcond)
{
    int ret;

    if (pcond == NULL)
    {
        LOGE("invalid parameters\n");
        return EN_UTILS_ERR_FAILED;
    }

    ret = pthread_mutex_lock(&pcond->mutex);
    if (ret != 0)
    {
        LOGE("mutex lock error");
        return EN_UTILS_ERR_FAILED;
    }

    if (pcond->released)
    {
        LOGE("cond has been released");
        return EN_UTILS_OK;
    }

    pcond->released = 1;
    ret = pthread_mutex_unlock(&pcond->mutex);
    if (ret != 0)
    {
        LOGE("mutex unlock error");
        return EN_UTILS_ERR_FAILED;
    }

    ret = pthread_mutex_destroy(&pcond->mutex);
    if (ret != 0)
    {
        LOGE("mute destroy failed:%s", strerror(ret));
        return EN_UTILS_ERR_FAILED;
    }

    ret = pthread_cond_destroy(&pcond->cond_data_available);
    if (ret != 0)
    {
        LOGE("mute destroy failed:%s", strerror(ret));
        return EN_UTILS_ERR_FAILED;
    }


    return EN_UTILS_OK;
}

utils_status_te utils_cond_timedwait(condition_val_ts *pcond, unsigned int u32WaitTimeout)
{
    int ret;
    struct timeval sNow;
    struct timespec sTimeout;

    if (pcond == NULL)
    {
        LOGE("invalid parameters\n");
        return EN_UTILS_ERR_FAILED;
    }

    ret = pthread_mutex_lock(&pcond->mutex);
    if (ret != 0)
    {
        LOGE("mute lock failed:%s", strerror(ret));
        return EN_UTILS_ERR_FAILED;
    }

    if (pcond->released)
    {
        usleep(100000);
        LOGE("cond has been released");
        return EN_UTILS_ERR_FAILED;
    }

    memset(&sNow, 0, sizeof(struct timeval));
    gettimeofday(&sNow, NULL);
    sTimeout.tv_sec = sNow.tv_sec + (u32WaitTimeout / 1000);
    sTimeout.tv_nsec = (sNow.tv_usec + ((u32WaitTimeout % 1000) * 1000)) * 1000;

    if (sTimeout.tv_nsec > 1000000000)
    {
        sTimeout.tv_sec++;
        sTimeout.tv_nsec -= 1000000000;
    }

    while (pcond->val == NULL)
    {
        ret = pthread_cond_timedwait(&pcond->cond_data_available, &pcond->mutex, &sTimeout);
        if (ret == 0)
        {
            ret = pthread_mutex_unlock(&pcond->mutex);
            if (ret != 0)
            {
                LOGE("mutex unlock error");
                return EN_UTILS_ERR_FAILED;
            }
            return EN_UTILS_OK;
        }
        else if (ret == ETIMEDOUT)
        {
            ret = pthread_mutex_unlock(&pcond->mutex);
            if (ret != 0)
            {
                LOGE("mutex unlock error");
                return EN_UTILS_ERR_FAILED;
            }
            return EN_UTILS_ERR_TIMEOUT;
        }
        else
        {
            LOGE("cond wait failed:%s", strerror(ret));
            ret = pthread_mutex_unlock(&pcond->mutex);
            if (ret != 0)
            {
                LOGE("mutex unlock error");
                return EN_UTILS_ERR_FAILED;
            }
            return EN_UTILS_ERR_FAILED;
        }
    }

    ret = pthread_mutex_unlock(&pcond->mutex);
    if (ret != 0)
    {
        LOGE("mute unlock failed:%s", strerror(ret));
        return EN_UTILS_ERR_FAILED;
    }

    return EN_UTILS_OK;
}

utils_status_te utils_cond_signal(condition_val_ts *pcond, void *newval)
{
    int ret;

    if (pcond == NULL)
    {
        LOGE("invalid parameters\n");
        return EN_UTILS_ERR_FAILED;
    }

    ret = pthread_mutex_lock(&pcond->mutex);
    if (ret != 0)
    {
        LOGE("mute lock failed:%s", strerror(ret));
        return EN_UTILS_ERR_FAILED;
    }

    if (pcond->released)
    {
        LOGE("cond has been released");
        return EN_UTILS_ERR_FAILED;
    }
    pcond->val = newval;

    ret = pthread_mutex_unlock(&pcond->mutex);
    if (ret != 0)
    {
        LOGE("mute unlock failed:%s", strerror(ret));
        return EN_UTILS_ERR_FAILED;
    }

    ret = pthread_cond_signal(&pcond->cond_data_available);
    if (ret != 0)
    {
        LOGE("cond signal failed:%s", strerror(ret));
        return EN_UTILS_ERR_FAILED;
    }

    return EN_UTILS_OK;
}

utils_status_te utils_cond_broadcast(condition_val_ts *pcond, void *newval)
{
    int ret;

    if (pcond == NULL)
    {
        LOGE("invalid parameters\n");
        return EN_UTILS_ERR_FAILED;
    }

    ret = pthread_mutex_lock(&pcond->mutex);
    if (ret != 0)
    {
        LOGE("mute lock failed:%s", strerror(ret));
        return EN_UTILS_ERR_FAILED;
    }

    if (pcond->released)
    {
        LOGE("cond has been released");
        return EN_UTILS_ERR_FAILED;
    }
    pcond->val = newval;

    ret = pthread_mutex_unlock(&pcond->mutex);
    if (ret != 0)
    {
        LOGE("mute unlock failed:%s", strerror(ret));
        return EN_UTILS_ERR_FAILED;
    }

    ret = pthread_cond_broadcast(&pcond->cond_data_available);
    if (ret != 0)
    {
        LOGE("cond signal failed:%s", strerror(ret));
        return EN_UTILS_ERR_FAILED;
    }

    return EN_UTILS_OK;
}

utils_status_te utils_cond_wait_until(condition_val_ts *pcond, unsigned int millisecond)
{
    int counter = 0;

    while (1)
    {
        if (OK != utils_cond_timedwait(pcond, 1))
        {
            if (counter++ > millisecond)
            {
                //LOGE( "contion Wait error or timeout\n");
                return EN_UTILS_ERR_FAILED;
            }
        }
        else
        {
            break;
        }
    }

    return EN_UTILS_OK;
}

utils_status_te utils_mem_dup( void **dst, void *src, unsigned int size )
{
    void *dup;

    dup = malloc(size);
    if ( NULL == dup )
    {
        LOGE("no mem");
        return EN_UTILS_ERR_NOMEM;
    }

    memmove(dup, src, size);
    *dst = dup;
    return EN_UTILS_OK;
}

char *utils_fileread(char *path, char *buf, size_t *length)
{
    int ret;
    int rn, readed;
    int fd = 0;
    char *rptr;
    struct stat fbuf;

    if (path == NULL)
    {
        LOGE("parameters error");
        rptr = NULL;
        goto error;
    }

    fd = open(path, O_RDONLY);
    if (fd == -1)
    {
        LOGE("open file error");
        rptr = NULL;
        goto error;
    }

    ret = fstat(fd, &fbuf);
    if (ret == -1)
    {
        LOGE("fstat error");
        rptr = NULL;
        goto error;
    }

    //调用者未分配了存储空间
    if (buf == NULL)
    {
        rptr = malloc(fbuf.st_size + 1);
        if (rptr == NULL)
        {
            LOGE("malloc error");
            rptr = NULL;
            goto error;
        }
    }
    else
    {
        //调用者分配的空间过小
        if (*length < fbuf.st_size)
        {
            LOGE("allocated mem too small");
            rptr = NULL;
            goto error;
        }
        else
        {
            rptr = buf;
        }

    }

    readed = 0;
    do
    {
        rn = read(fd, rptr + readed, fbuf.st_size - readed);
        if (rn == -1)
        {
            if (errno == EINTR)
            {
                continue;
            }
            else
            {
                LOGE("read error");
                rptr = NULL;
                goto error;
            }
        }
        readed += rn;
    } while (readed < fbuf.st_size);

    *length = fbuf.st_size;
    rptr[fbuf.st_size] = 0;


    if (fd > 0)
    {
        close(fd);
    }
    return rptr;

error:
    //出错，释放由本函数分配的内存
    if (buf != NULL)
    {
        if (rptr != NULL)
        {
            free(rptr);
            rptr = NULL;
        }
    }

    if (fd > 0)
    {
        close(fd);
    }
    return rptr;
}

int utils_filewrite(char *path, char *buf, size_t length)
{
    int fd;
    int wn, writed;

    if (!buf || !path || !length)
    {
        LOGE("invalid parameters");
        return FAILED;
    }

    fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
    if (fd == -1)
    {
        LOGE("file open error:%s", strerror(errno));
        return FAILED;
    }

    writed = 0;
    do
    {
        wn = write(fd, buf + writed, length - writed);
        if (wn == -1)
        {
            if (errno == EINTR)
            {
                continue;
            }
            else
            {
                LOGE("file write error:%s", strerror(errno));
                close(fd);
                return FAILED;
            }
        }
        writed += wn;
    } while (writed < length);

    if (-1 == fsync(fd))
    {
        LOGE("file fsync error:%s", strerror(errno));
        close(fd);
        return FAILED;
    }

    close(fd);

    return OK;
}

// host long 64 to network
unsigned long long htonll(unsigned long long host)
{
    unsigned long long ret;
    unsigned long high, low;

    low = host & 0xFFFFFFFF;
    high = (host >> 32u) & 0xFFFFFFFF;
    low = htonl(low);
    high = htonl(high);


    ret = low;
    ret <<= 32u;
    ret |= high;

    return ret;
}

//network to host long 64
unsigned long long ntohll( unsigned long long host )
{
    unsigned long long ret;
    unsigned long high, low;

    low = host & 0xFFFFFFFF;
    high = (host >> 32u) & 0xFFFFFFFF;
    low = ntohl(low);
    high = ntohl(high);

    ret = low;
    ret <<= 32u;
    ret |= high;
    return ret;
}


int base64_encode(const void *in, int len, char *out)
{
    BIO *b64, *bio;
    BUF_MEM *bptr = NULL;
    size_t size = 0;

    if (in == NULL || out == NULL)
        return -1;

    b64 = BIO_new(BIO_f_base64());
    bio = BIO_new(BIO_s_mem());
    bio = BIO_push(b64, bio);

    BIO_write(bio, in, len);
    BIO_flush(bio);

    BIO_get_mem_ptr(bio, &bptr);
    memcpy(out, bptr->data, bptr->length);
    out[bptr->length] = '\0';
    size = bptr->length;

    BIO_free_all(bio);
    return size;
}

int base64_decode(const char *in, int len, void *out)
{
    BIO *b64, *bio;
    int size = 0;

    if (in == NULL || out == NULL)
        return -1;

    b64 = BIO_new(BIO_f_base64());
    BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);

    bio = BIO_new_mem_buf(in, len);
    bio = BIO_push(b64, bio);

    size = BIO_read(bio, out, len);

    BIO_free_all(bio);
    return size;
}
